package com.yunshipei.core.download;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.yunshipei.core.R;
import com.yunshipei.core.common.XCloudException;
import com.yunshipei.core.download.progress.ProgressListener;
import com.yunshipei.core.download.progress.ProgressResponseBody;
import com.yunshipei.core.model.HttpAuthModel;
import com.yunshipei.core.model.XDownloadModel;
import com.yunshipei.core.net.EnterClient;
import com.yunshipei.core.net.NTLMAuthenticator;
import com.yunshipei.core.utils.FileUtils;
import com.yunshipei.core.utils.YspLogUtils;

import org.json.JSONArray;
import org.json.JSONObject;
import org.reactivestreams.Publisher;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Streaming;
import retrofit2.http.Url;

/**
 * 浏览器下载器，下载成功直接用本地应用打开，该Services在独立进程中
 */
public class YspDownload extends Service {

    private static final String EXTRA_DOWNLOAD_MODEL = "downloadModel";
    private static final String EXTRA_PROXY_IP = "download_proxy_ip";
    private static final String EXTRA_PROXY_PORT = "download_proxy_port";
    private static final String EXTRA_AUTHORIZATION = "download_authorization";

    public static final String ACTION_START_DOWNLOAD = "android.intent.action.ENTERPLORER_DOWNLOAD_SERVICES";


    private static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory().getPath() + "/yunshipei/download/";

    public static void startDownload(Context context, XDownloadModel xDownloadModel) {
        context.startService(new Intent(ACTION_START_DOWNLOAD).setPackage(context.getPackageName()).putExtra(EXTRA_DOWNLOAD_MODEL, xDownloadModel));
    }

    public static void startDownload(Context context, XDownloadModel xDownloadModel, String proxyIP, int proxyPort, HttpAuthModel httpAuthModel) {
        context.startService(new Intent(ACTION_START_DOWNLOAD).setPackage(context.getPackageName()).putExtra(EXTRA_DOWNLOAD_MODEL, xDownloadModel).putExtra(EXTRA_PROXY_IP, proxyIP).putExtra(EXTRA_PROXY_PORT, proxyPort).putExtra(EXTRA_AUTHORIZATION, httpAuthModel));
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    private NotificationManagerCompat mNotificationManager;

    @Override
    public void onCreate() {
        super.onCreate();
        mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            String action = intent.getAction();
            if (ACTION_START_DOWNLOAD.equals(action)) {
                startDownload(intent);
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private synchronized void startDownload(final Intent intent) {
        final XDownloadModel xDownloadModel = (XDownloadModel) intent.getSerializableExtra(EXTRA_DOWNLOAD_MODEL);

//        final Request request = new Request.Builder().addHeader("Accept-Encoding", "gzip").url(downloadUrl).build();


        Log.i("_ser_download_model", "process:" + Process.myPid() + "\n" + xDownloadModel.toString());
        YspLogUtils.d("service_process:" + Process.myPid() + "\n" + xDownloadModel.toString());
        String timeStamp = String.valueOf(System.currentTimeMillis());
        final int notificationID = Integer.parseInt(timeStamp.substring(timeStamp.length() - 6, timeStamp.length() - 1));
        Flowable.create(new FlowableOnSubscribe<Flowable<retrofit2.Response<ResponseBody>>>() {
            @Override
            public void subscribe(FlowableEmitter<Flowable<retrofit2.Response<ResponseBody>>> e) throws Exception {
                OkHttpClient.Builder downloadClientBuilder = new OkHttpClient.Builder();
                //设置cookie和ua
                final String cookieStr = xDownloadModel.getCookie();
                final String userAgent = xDownloadModel.getUserAgent();
                if (!TextUtils.isEmpty(cookieStr) || !TextUtils.isEmpty(userAgent)) {
                    downloadClientBuilder.addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request.Builder builder = chain.request().newBuilder();
                            if (!TextUtils.isEmpty(cookieStr)) {
                                builder.addHeader("Cookie", cookieStr);
                            }
                            if (!TextUtils.isEmpty(userAgent)) {
                                builder.addHeader("User-Agent", userAgent);
                            }
                            return chain.proceed(builder.build());
                        }
                    });
                }
                //添加通知进度监听
                final DownloadProgressListener progressListener = new DownloadProgressListener(YspDownload.this, xDownloadModel.getFileName(), mNotificationManager, notificationID);
                downloadClientBuilder.addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Response originalResponse = chain.proceed(chain.request());
                        return originalResponse.newBuilder()
                                .body(new ProgressResponseBody(originalResponse.body(), progressListener))
                                .build();
                    }
                });
                //设置AD认证
                HttpAuthModel httpAuthModel = intent.getParcelableExtra(EXTRA_AUTHORIZATION);
                if (httpAuthModel != null) {
                    YspLogUtils.d("ntlm auth:" + httpAuthModel.toString());
                    downloadClientBuilder.authenticator(new NTLMAuthenticator(httpAuthModel.getUserName(), httpAuthModel.getPassword()));
                }
                //设置代理
                String proxyIP = intent.getStringExtra(EXTRA_PROXY_IP);
                int proxyPort = intent.getIntExtra(EXTRA_PROXY_PORT, 0);
                if (!TextUtils.isEmpty(proxyIP) && proxyPort > 0) {
                    YspLogUtils.d("proxy:" + proxyIP + "\n" + proxyPort);
                    SocketAddress sa = new InetSocketAddress(proxyIP, proxyPort);
                    //定义代理，此处的Proxy是源自java.net
                    Proxy proxy = new Proxy(Proxy.Type.HTTP, sa);
                    downloadClientBuilder.proxy(proxy);
                }

                //https://mworkflow.cofcoko.com:4431/install/app-measurement-release.apk
                OkHttpClient okHttpClient = downloadClientBuilder.build();
                EnterClient.getInstances().initSSLClient(okHttpClient);
                String downloadUrl = xDownloadModel.getUrl();
                Flowable<retrofit2.Response<ResponseBody>> downloadFlowable = null;
                DownloadApi downloadApi = new Retrofit.Builder().client(okHttpClient).baseUrl("http://127.0.0.1:8080").addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build().create(DownloadApi.class);
                String method = xDownloadModel.getMethod();
                switch (method.toUpperCase()) {
                    case "GET":
                        downloadFlowable = downloadApi.get(downloadUrl);
                        break;
                    case "POST":
                        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
                        JSONArray jsonArray = new JSONArray(xDownloadModel.getFormData());
                        for (int i = 9; i < jsonArray.length(); i++) {
                            JSONObject object = jsonArray.optJSONObject(i);
                            if (object == null) continue;
                            builder.addFormDataPart(object.optString("key"), object.optString("value"));
                        }
                        downloadFlowable = downloadApi.post(downloadUrl, builder.build());
                        break;
                }
                e.onNext(downloadFlowable);
            }
        }, BackpressureStrategy.BUFFER).flatMap(new Function<Flowable<retrofit2.Response<ResponseBody>>, Publisher<retrofit2.Response<ResponseBody>>>() {
            @Override
            public Publisher<retrofit2.Response<ResponseBody>> apply(Flowable<retrofit2.Response<ResponseBody>> responseFlowable) throws Exception {
                return responseFlowable;
            }
        }).flatMap(new Function<retrofit2.Response<ResponseBody>, Publisher<String>>() {
            @Override
            public Publisher<String> apply(retrofit2.Response<ResponseBody> response) throws Exception {
                int code = response.code();
                if (code == 200) {
                    String fileName = xDownloadModel.getFileName();
                    boolean save = FileUtils.writeToFile(DOWNLOAD_PATH, fileName, response.body().byteStream());
                    if (save) {
                        return Flowable.just(DOWNLOAD_PATH + fileName);
                    } else {
                        return Flowable.error(new XCloudException("文件保存失败..."));
                    }
                }
                return Flowable.error(new XCloudException("下载失败，状态码：" + code));
            }
        }).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String fileName) throws Exception {
                mNotificationManager.cancel(notificationID);
                FileUtils.openFile(YspDownload.this, fileName);
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                mNotificationManager.cancel(notificationID);
                Toast.makeText(YspDownload.this, throwable instanceof XCloudException ? throwable.getMessage() : "下载失败...\n" + throwable.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private interface DownloadApi {
        @GET
        @Streaming
        Flowable<retrofit2.Response<ResponseBody>> get(@Url String url);

        @POST
        @Streaming
        Flowable<retrofit2.Response<ResponseBody>> post(@Url String url, @Body RequestBody requestBody);
    }

    private static class DownloadProgressListener implements ProgressListener {

        private NotificationManagerCompat mNotificationManager;
        private int mNotificationID;
        private NotificationCompat.Builder mBuilder;

        private Context context;
        private String fileName;

        DownloadProgressListener(final Context context, final String fileName, NotificationManagerCompat notificationManagerCompat, int notificationID) {
            this.context = context;
            this.fileName = fileName;
            mNotificationManager = notificationManagerCompat;
            mNotificationID = notificationID;
            AndroidSchedulers.mainThread().createWorker().schedule(new Runnable() {
                @Override
                public void run() {
                    mBuilder = new NotificationCompat.Builder(context);
                    mBuilder.setAutoCancel(true);
                    mBuilder.setSmallIcon(R.drawable.sdk_logo)
                            .setContentTitle(fileName)
                            .setProgress(100, 0, false).setContentText("开始下载...");
                    mNotificationManager.notify(mNotificationID, mBuilder.build());
                    Toast.makeText(context, "开始下载:" + fileName, Toast.LENGTH_SHORT).show();
                }
            });
        }

        private long lastTime = 0;

        @Override
        public void onProgress(long currentBytes, long contentLength, boolean done) {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastTime > 2000) {
                final int progress = currentBytes <= 0 ? 0 : (int) ((100 * currentBytes) / contentLength);
                mBuilder.setProgress(100, progress, false).setContentInfo(progress + "%").setContentText("正在下载...");
                mNotificationManager.notify(mNotificationID, mBuilder.build());
                lastTime = currentTime;
            }
            if (contentLength == currentBytes) {
                AndroidSchedulers.mainThread().createWorker().schedule(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, fileName + "已下载完毕", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    }
}
